import os
import subprocess
import sys
import shutil
from typing import List, Optional, Dict, Tuple
import tkinter as tk
from tkinter import ttk, filedialog, messagebox


class TSMergerGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("TS视频合并工具 - 自定义命名+进度日志")
        self.root.geometry("1300x900")
        self.root.resizable(True, True)

        # 全局变量
        self.default_root_path = r"E:\视频录制"  # 固定根目录
        # 存储结构：{E:\视频录制\下级文件夹: [(TS文件所在文件夹路径, TS文件列表), ...]}
        self.found_targets: Dict[str, List[Tuple[str, List[str]]]] = {}
        self.selected_parent_folder: Optional[str] = None  # 当前选择的E:\视频录制下级文件夹
        self.save_mode = tk.StringVar(value="same_dir")  # same_dir（同级）/ custom_dir（自定义）
        self.custom_save_path: Optional[str] = self.default_root_path  # 自定义保存路径
        self.delete_after_merge = tk.BooleanVar(value=True)  # 合并后自动删除文件夹（默认开启）
        self.custom_filename_var = tk.StringVar()  # 自定义文件名变量（不含后缀）

        # 初始化默认路径（创建E:\视频录制）
        self._init_default_paths()

        # 构建界面
        self._setup_ui()

        # 启动后自动扫描TS文件夹
        self.scan_ts_folders(auto_scan=True)

    def _init_default_paths(self):
        """初始化默认路径（创建E:\视频录制）"""
        try:
            if not os.path.exists(self.default_root_path):
                os.makedirs(self.default_root_path)
                messagebox.showinfo(
                    "路径创建成功",
                    f"已自动创建默认根目录：{self.default_root_path}\n"
                    "请将包含TS文件的文件夹放入该目录下（支持嵌套子文件夹）"
                )
        except Exception as e:
            messagebox.showerror(
                "路径创建失败",
                f"无法创建默认根目录「E:\\视频录制」\n"
                f"错误原因：{str(e)}\n"
                "程序将无法自动扫描，请手动选择根目录"
            )
            self.default_root_path = ""

    def _setup_ui(self):
        """构建界面（修复save_frame+新增进度日志框）"""
        # 1. 顶部功能区
        func_frame = ttk.Frame(self.root, padding="10", relief=tk.RAISED)
        func_frame.pack(fill=tk.X, expand=False, anchor=tk.N)

        # 搜索目录相关
        ttk.Label(func_frame, text="搜索根目录:", font=("Arial", 10, "bold")).grid(row=0, column=0, padx=5, pady=5,
                                                                              sticky=tk.E)
        self.root_folder_label = ttk.Label(
            func_frame,
            text=self.default_root_path if self.default_root_path else "未找到E:\\视频录制",
            width=45,
            wraplength=350,
            foreground="green" if self.default_root_path else "red"
        )
        self.root_folder_label.grid(row=0, column=1, padx=5, pady=5, sticky=tk.W)

        self.open_root_btn = ttk.Button(
            func_frame,
            text="打开搜索目录",
            command=lambda: self._open_folder(self.default_root_path if self.default_root_path else None),
            style="Accent.TButton"
        )
        self.open_root_btn.grid(row=0, column=2, padx=5, pady=5, sticky=tk.W)

        self.rescan_btn = ttk.Button(
            func_frame,
            text="重新扫描所有文件夹",
            command=self.scan_ts_folders,
            style="Accent.TButton"
        )
        self.rescan_btn.grid(row=0, column=3, padx=5, pady=5, sticky=tk.W)

        # 2. 保存模式、清理设置与自定义命名区（修复：添加self.使save_frame成为实例属性）
        self.save_frame = ttk.Frame(self.root, padding="10")  # 关键修复：添加self.
        self.save_frame.pack(fill=tk.X, expand=False, anchor=tk.N)

        # 第一行：保存模式 + 清理设置
        ttk.Label(self.save_frame, text="保存模式:", font=("Arial", 10, "bold")).grid(row=0, column=0, padx=5, pady=5,
                                                                                  sticky=tk.E)
        ttk.Radiobutton(
            self.save_frame,
            text="保存到E:\\视频录制目录",
            variable=self.save_mode,
            value="same_dir",
            command=self.update_save_mode_ui
        ).grid(row=0, column=1, padx=5, pady=5, sticky=tk.W)
        ttk.Radiobutton(
            self.save_frame,
            text="统一保存到自定义目录",
            variable=self.save_mode,
            value="custom_dir",
            command=self.update_save_mode_ui
        ).grid(row=0, column=2, padx=5, pady=5, sticky=tk.W)

        self.select_custom_save_btn = ttk.Button(
            self.save_frame,
            text="选择自定义保存路径",
            command=self.select_custom_save_path,
            state=tk.DISABLED,
            style="Accent.TButton"
        )
        self.select_custom_save_btn.grid(row=0, column=3, padx=5, pady=5, sticky=tk.W)

        ttk.Label(self.save_frame, text="合并后操作:", font=("Arial", 10, "bold")).grid(row=0, column=4, padx=5, pady=5,
                                                                                   sticky=tk.E)
        ttk.Checkbutton(
            self.save_frame,
            text="自动删除原文件夹（合并成功后）",
            variable=self.delete_after_merge,
            onvalue=True,
            offvalue=False
        ).grid(row=0, column=5, padx=5, pady=5, sticky=tk.W)

        # 第二行：自定义输出文件名
        ttk.Label(self.save_frame, text="输出文件名:", font=("Arial", 10, "bold")).grid(row=1, column=0, padx=5, pady=5,
                                                                                   sticky=tk.E)
        self.filename_entry = ttk.Entry(
            self.save_frame,
            textvariable=self.custom_filename_var,
            width=50,
            font=("Arial", 10)
        )
        self.filename_entry.grid(row=1, column=1, padx=5, pady=5, sticky=tk.W + tk.E, columnspan=3)
        ttk.Label(self.save_frame, text=".mp4", font=("Arial", 10)).grid(row=1, column=4, padx=5, pady=5, sticky=tk.W)

        # 第三行：输出路径预览
        ttk.Label(self.save_frame, text="最终输出路径:", font=("Arial", 10, "bold")).grid(row=2, column=0, padx=5, pady=5,
                                                                                    sticky=tk.E)
        self.output_path_label = ttk.Label(
            self.save_frame,
            text="请选择文件夹后设置文件名",
            width=60,
            wraplength=500,
            foreground="#333",
            font=("Arial", 10)
        )
        self.output_path_label.grid(row=2, column=1, padx=5, pady=5, sticky=tk.W, columnspan=4)

        self.open_output_btn = ttk.Button(
            self.save_frame,
            text="查看输出路径",
            command=self.open_current_output_path,
            state=tk.DISABLED,
            style="Accent.TButton"
        )
        self.open_output_btn.grid(row=2, column=5, padx=5, pady=5, sticky=tk.W)

        # 3. 文件夹选择与合并按钮区
        merge_frame = ttk.Frame(self.root, padding="10", relief=tk.GROOVE)
        merge_frame.pack(fill=tk.X, expand=False, anchor=tk.N)

        ttk.Label(merge_frame, text="选择要合并的文件夹:", font=("Arial", 10, "bold")).grid(row=0, column=0, padx=5, pady=5,
                                                                                   sticky=tk.E)
        self.parent_folder_combobox = ttk.Combobox(merge_frame, width=60, state="readonly")
        self.parent_folder_combobox.grid(row=0, column=1, padx=5, pady=5, sticky=tk.W + tk.E, columnspan=2)
        self.parent_folder_combobox.bind("<<ComboboxSelected>>", self.on_parent_folder_selected)

        # 单个合并按钮
        self.merge_single_btn = ttk.Button(
            merge_frame,
            text="开始合并当前选择文件夹",
            command=self.start_merge_single,
            state=tk.DISABLED,
            style="Success.TButton"
        )
        self.merge_single_btn.grid(row=0, column=3, padx=5, pady=5, sticky=tk.W)

        # 批量合并按钮
        self.merge_all_btn = ttk.Button(
            merge_frame,
            text="批量合并所有文件夹（合并后自动清理）",
            command=self.start_merge_all,
            state=tk.DISABLED,
            style="Warning.TButton"
        )
        self.merge_all_btn.grid(row=0, column=4, padx=5, pady=5, sticky=tk.E)

        # 4. 中间文件列表区
        list_frame = ttk.Frame(self.root, padding="10")
        list_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 10))

        title_frame = ttk.Frame(list_frame)
        title_frame.pack(fill=tk.X, expand=False, anchor=tk.W)
        ttk.Label(title_frame, text="找到的TS文件（按数字顺序）:", font=("Arial", 11, "bold")).pack(side=tk.LEFT)
        self.file_count_label = ttk.Label(title_frame, text="(0个文件)", font=("Arial", 10))
        self.file_count_label.pack(side=tk.LEFT, padx=10)
        self.ts_folder_path_label = ttk.Label(title_frame, text="", font=("Arial", 9), foreground="#666")
        self.ts_folder_path_label.pack(side=tk.LEFT, padx=10)

        scrollbar = ttk.Scrollbar(list_frame)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        self.file_listbox = tk.Listbox(
            list_frame,
            yscrollcommand=scrollbar.set,
            font=("Arial", 10),
            selectbackground="#e0e0e0",
            bg="white",
            height=10
        )
        self.file_listbox.pack(fill=tk.BOTH, expand=True, padx=(0, 5))
        scrollbar.config(command=self.file_listbox.yview)

        # 5. 合并进度日志区（新增核心功能）
        log_frame = ttk.Frame(self.root, padding="10")
        log_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 10))

        ttk.Label(log_frame, text="合并进度日志（实时更新）:", font=("Arial", 11, "bold")).pack(anchor=tk.W, padx=5)
        self.log_text = tk.Text(
            log_frame,
            font=("Consolas", 10),
            bg="#f8f8f8",
            fg="#333",
            wrap=tk.WORD,
            state=tk.DISABLED
        )
        log_scrollbar = ttk.Scrollbar(log_frame, command=self.log_text.yview)
        self.log_text.configure(yscrollcommand=log_scrollbar.set)

        self.log_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5, side=tk.LEFT)
        log_scrollbar.pack(fill=tk.Y, padx=(0, 5), pady=5)

        # 6. 底部状态区
        status_frame = ttk.Frame(self.root, padding="10")
        status_frame.pack(fill=tk.X, expand=False, anchor=tk.S)

        self.status_label = ttk.Label(
            status_frame,
            text="正在初始化并扫描TS文件夹...",
            font=("Arial", 10)
        )
        self.status_label.pack(anchor=tk.W, pady=5)

        self.progress_var = tk.DoubleVar()
        self.progress_bar = ttk.Progressbar(
            status_frame,
            variable=self.progress_var,
            maximum=100,
            mode="determinate"
        )
        self.progress_bar.pack(fill=tk.X, padx=5, pady=5, expand=True)
        self.progress_bar.pack_forget()

        # 样式配置
        self.root.style = ttk.Style()
        self.root.style.configure("Accent.TButton", font=("Arial", 10, "bold"), foreground="white",
                                  background="#2196F3")
        self.root.style.configure("Success.TButton", font=("Arial", 11, "bold"), foreground="white",
                                  background="#4CAF50", padding=10)
        self.root.style.configure("Warning.TButton", font=("Arial", 10, "bold"), foreground="white",
                                  background="#FF5722")

        # 绑定文件名输入变化事件
        self.custom_filename_var.trace("w", self._on_filename_changed)

    def _log(self, message: str, level: str = "INFO"):
        """添加日志到日志框（实时更新）"""
        self.log_text.config(state=tk.NORMAL)
        # 日志级别颜色：INFO=黑色，SUCCESS=绿色，WARNING=橙色，ERROR=红色
        color_map = {
            "INFO": "#333333",
            "SUCCESS": "#008000",
            "WARNING": "#FF8C00",
            "ERROR": "#FF0000"
        }
        color = color_map.get(level.upper(), "#333333")

        # 添加时间戳和日志内容
        import datetime
        timestamp = datetime.datetime.now().strftime("%H:%M:%S")
        self.log_text.insert(tk.END, f"[{timestamp}] [{level.upper()}] {message}\n", level.lower())

        # 自动滚动到最新日志
        self.log_text.see(tk.END)
        self.log_text.config(state=tk.DISABLED)
        self.root.update_idletasks()  # 强制刷新界面

    def _clear_log(self):
        """清空日志框"""
        self.log_text.config(state=tk.NORMAL)
        self.log_text.delete(1.0, tk.END)
        self.log_text.config(state=tk.DISABLED)

    def _get_sorted_ts_files(self, folder_path: str) -> List[str]:
        """查找单个文件夹下的TS文件并按数字排序"""
        self._log(f"正在扫描TS文件：{folder_path}")
        ts_files = []
        for filename in os.listdir(folder_path):
            file_path = os.path.join(folder_path, filename)
            if os.path.isfile(file_path) and filename.lower().endswith(".ts"):
                try:
                    num_part = ''.join(filter(str.isdigit, filename))
                    if not num_part:
                        continue
                    file_num = int(num_part)
                    ts_files.append((file_num, os.path.abspath(file_path)))
                except Exception as e:
                    self._log(f"跳过异常文件 {filename}：{str(e)}", "WARNING")

        ts_files.sort(key=lambda x: x[0])
        self._log(f"扫描完成，找到 {len(ts_files)} 个有效TS文件", "SUCCESS")
        return [path for _, path in ts_files]

    def _scan_parent_folder(self, parent_folder: str) -> List[Tuple[str, List[str]]]:
        """扫描单个上级文件夹（E:\视频录制的直接下级）下的所有TS文件"""
        self._log(f"开始扫描上级文件夹：{parent_folder}")
        ts_targets = []
        for root, dirs, files in os.walk(parent_folder):
            ts_files = self._get_sorted_ts_files(root)
            if ts_files:
                ts_targets.append((root, ts_files))
                self._log(f"在子目录 {root} 中找到 {len(ts_files)} 个TS文件")
        return ts_targets

    def scan_ts_folders(self, auto_scan: bool = False):
        """扫描E:\视频录制的直接下级文件夹，查找包含TS文件的目标"""
        self._clear_log()  # 扫描前清空日志
        if not self.default_root_path or not os.path.exists(self.default_root_path):
            if not auto_scan:
                self.default_root_path = filedialog.askdirectory(title="选择TS文件根目录（直接下级文件夹为命名依据）")
                if not self.default_root_path:
                    self._log("未选择根目录，扫描取消", "WARNING")
                    messagebox.showwarning("警告", "未选择根目录，扫描取消")
                    return
            else:
                self._log("未找到根目录「E:\\视频录制」，无法自动扫描", "ERROR")
                messagebox.showwarning("警告", "未找到根目录「E:\\视频录制」，无法自动扫描")
                self.status_label.config(text="未找到根目录，无法扫描")
                return

        self.root_folder_label.config(text=self.default_root_path, foreground="green")
        self.status_label.config(text=f"正在扫描：{self.default_root_path}（仅扫描直接下级文件夹）...")
        self._log(f"开始扫描根目录：{self.default_root_path}")
        self.root.update_idletasks()

        # 清空之前的结果
        self.found_targets.clear()
        self.parent_folder_combobox["values"] = []
        self.file_listbox.delete(0, tk.END)
        self.file_count_label.config(text="(0个文件)")
        self.ts_folder_path_label.config(text="")
        self.custom_filename_var.set("")
        self.output_path_label.config(text="请选择文件夹后设置文件名")
        self.open_output_btn.config(state=tk.DISABLED)
        self.merge_single_btn.config(state=tk.DISABLED)
        self.merge_all_btn.config(state=tk.DISABLED)

        try:
            # 只扫描E:\视频录制的直接下级文件夹
            total_parent_folders = 0
            for name in os.listdir(self.default_root_path):
                parent_folder = os.path.join(self.default_root_path, name)
                if os.path.isdir(parent_folder):  # 只处理直接下级文件夹
                    total_parent_folders += 1
                    self._log(f"扫描直接下级文件夹：{name}")
                    ts_targets = self._scan_parent_folder(parent_folder)
                    if ts_targets:
                        self.found_targets[parent_folder] = ts_targets
                        total_ts = sum(len(ts_files) for _, ts_files in ts_targets)
                        self._log(f"文件夹 {name} 包含 {total_ts} 个TS文件，添加为目标", "SUCCESS")

            if not self.found_targets:
                self._log("未在E:\\视频录制的直接下级文件夹中找到任何TS文件！", "WARNING")
                messagebox.showwarning("警告", "未在E:\\视频录制的直接下级文件夹中找到任何TS文件！")
                self.status_label.config(text=f"扫描完成 - 未找到TS文件（共扫描 {total_parent_folders} 个直接下级文件夹）")
            else:
                # 更新下拉框
                combobox_values = []
                for parent_folder in self.found_targets.keys():
                    parent_folder_name = os.path.basename(parent_folder)
                    total_ts_count = sum(len(ts_files) for _, ts_files in self.found_targets[parent_folder])
                    display_text = f"{parent_folder_name}（共 {total_ts_count} 个TS文件）"
                    combobox_values.append((parent_folder, display_text))

                combobox_values.sort(key=lambda x: x[0])
                self.parent_folder_combobox["values"] = [v[1] for v in combobox_values]
                self.parent_folder_combobox.current(0)
                self.selected_parent_folder = combobox_values[0][0]
                self._update_file_list(self.selected_parent_folder)
                self._update_default_filename()
                self._update_output_path_display()

                # 启用按钮
                self.merge_single_btn.config(state=tk.NORMAL)
                self.merge_all_btn.config(state=tk.NORMAL)
                self.open_output_btn.config(state=tk.NORMAL)

                self.status_label.config(
                    text=f"扫描完成 - 找到 {len(self.found_targets)} 个目标文件夹（共扫描 {total_parent_folders} 个直接下级文件夹）"
                )
                self._log(f"扫描完成！找到 {len(self.found_targets)} 个目标文件夹", "SUCCESS")

        except Exception as e:
            self._log(f"扫描文件夹失败：{str(e)}", "ERROR")
            messagebox.showerror("错误", f"扫描文件夹失败：{str(e)}")
            self.status_label.config(text=f"扫描失败：{str(e)}")

    def _update_file_list(self, parent_folder: str):
        """更新当前选择上级文件夹下的所有TS文件列表"""
        ts_targets = self.found_targets.get(parent_folder, [])
        self.file_listbox.delete(0, tk.END)

        total_ts_count = 0
        ts_folder_paths = []
        for ts_folder, ts_files in ts_targets:
            ts_folder_paths.append(ts_folder)
            for i, file_path in enumerate(ts_files, 1):
                filename = os.path.basename(file_path)
                self.file_listbox.insert(tk.END, f"【{os.path.basename(ts_folder)}】{i:03d}. {filename}")
            total_ts_count += len(ts_files)

        self.file_count_label.config(text=f"({total_ts_count}个文件)")
        if ts_folder_paths:
            paths_text = " | ".join([os.path.basename(p) for p in ts_folder_paths])
            self.ts_folder_path_label.config(text=f"TS文件所在目录：{paths_text}")
            self._log(f"当前选择文件夹包含 {len(ts_folder_paths)} 个子目录，共 {total_ts_count} 个TS文件")
        else:
            self.ts_folder_path_label.config(text="")

    def _update_default_filename(self):
        """更新默认文件名（E:\视频录制直接下级文件夹名）"""
        if self.selected_parent_folder:
            # 严格取E:\视频录制的直接下级文件夹名作为默认文件名
            default_filename = os.path.basename(self.selected_parent_folder)
            self.custom_filename_var.set(default_filename)
            self._log(f"自动填充默认文件名：{default_filename}")

    def _get_output_path(self, parent_folder: Optional[str] = None, custom_filename: Optional[str] = None) -> str:
        """获取输出路径（支持自定义文件名）"""
        # 优先使用传入的参数，否则使用当前选中状态
        current_parent = parent_folder or self.selected_parent_folder
        current_filename = custom_filename or self.custom_filename_var.get().strip()

        # 文件名校验（不能为空）
        if not current_filename and current_parent:
            current_filename = os.path.basename(current_parent)
        elif not current_filename:
            current_filename = "未命名"

        # 构建完整文件名（自动添加.mp4后缀）
        output_filename = f"{current_filename}.mp4"

        # 确定保存目录
        if self.save_mode.get() == "same_dir":
            output_dir = self.default_root_path  # 保存到E:\视频录制目录
        else:
            output_dir = self.custom_save_path

        output_path = os.path.join(output_dir, output_filename)

        # 处理文件名冲突（自动加序号）
        counter = 1
        while os.path.exists(output_path):
            output_filename = f"{current_filename}_{counter}.mp4"
            output_path = os.path.join(output_dir, output_filename)
            counter += 1

        self._log(f"生成输出路径：{output_path}")
        return output_path

    def _update_output_path_display(self):
        """更新输出路径预览"""
        if not self.selected_parent_folder:
            self.output_path_label.config(text="请选择文件夹后设置文件名")
            self.open_output_btn.config(state=tk.DISABLED)
            return

        output_path = self._get_output_path()
        # 路径过长时截取显示
        display_path = output_path if len(output_path) <= 60 else f"...{output_path[-57:]}"
        self.output_path_label.config(text=f"合并后文件：{display_path}")
        self.open_output_btn.config(state=tk.NORMAL)

    def _on_filename_changed(self, *args):
        """文件名输入变化时更新预览"""
        new_filename = self.custom_filename_var.get().strip()
        if new_filename:
            self._log(f"自定义文件名已修改为：{new_filename}")
        self._update_output_path_display()

    def on_parent_folder_selected(self, event):
        """选择下拉框中的上级文件夹"""
        selected_text = self.parent_folder_combobox.get()
        if not selected_text:
            return

        for parent_folder, display_text in zip(self.found_targets.keys(), self.parent_folder_combobox["values"]):
            if display_text == selected_text:
                self.selected_parent_folder = parent_folder
                self._update_file_list(parent_folder)
                self._update_default_filename()
                self._update_output_path_display()
                break

    def update_save_mode_ui(self):
        """根据保存模式更新UI状态"""
        # 初始化自定义保存路径标签
        if not hasattr(self, 'custom_save_label'):
            self.custom_save_label = ttk.Label(
                self.save_frame,
                text=self.custom_save_path,
                width=50,
                wraplength=400,
                font=("Arial", 10)
            )
            self.custom_save_label.grid(row=3, column=1, padx=5, pady=5, sticky=tk.W, columnspan=3)

        if self.save_mode.get() == "custom_dir":
            self.select_custom_save_btn.config(state=tk.NORMAL)
            self.custom_save_label.config(foreground="green")
            self._log("保存模式切换为：自定义目录")
        else:
            self.select_custom_save_btn.config(state=tk.DISABLED)
            self.custom_save_label.config(foreground="#666")
            self._log("保存模式切换为：E:\\视频录制目录")

        # 切换保存模式后更新输出路径预览
        self._update_output_path_display()

    def select_custom_save_path(self):
        """选择自定义保存路径"""
        save_folder = filedialog.askdirectory(title="选择统一保存路径")
        if not save_folder:
            self._log("未选择自定义保存路径，保持原设置", "WARNING")
            return

        self.custom_save_path = save_folder
        if not hasattr(self, 'custom_save_label'):
            self.custom_save_label = ttk.Label(
                self.save_frame,
                text=self.custom_save_path,
                width=50,
                wraplength=400,
                font=("Arial", 10)
            )
            self.custom_save_label.grid(row=3, column=1, padx=5, pady=5, sticky=tk.W, columnspan=3)
        else:
            self.custom_save_label.config(text=self.custom_save_path)

        self._update_output_path_display()
        self.status_label.config(text=f"自定义保存路径已更新：{save_folder}")
        self._log(f"自定义保存路径已更新为：{save_folder}", "SUCCESS")

    def open_current_output_path(self):
        """打开当前选中文件夹的输出路径"""
        if not self.selected_parent_folder:
            self._log("未选择文件夹，无法打开输出路径", "WARNING")
            messagebox.showwarning("警告", "请先选择文件夹！")
            return

        output_path = self._get_output_path()
        output_dir = os.path.dirname(output_path)

        if not os.path.exists(output_dir):
            self._log(f"输出目录不存在，正在创建：{output_dir}")
            os.makedirs(output_dir)

        self._log(f"打开输出路径：{output_dir}")
        self._open_folder(output_dir)

    def _create_temp_file_list(self, all_ts_files: List[str], temp_list_path: str) -> None:
        """创建所有TS文件的列表（支持多个子目录的TS文件合并）"""
        self._log(f"开始创建临时文件列表：{temp_list_path}")
        self._log(f"共需写入 {len(all_ts_files)} 个TS文件路径")
        try:
            with open(temp_list_path, "w", encoding="utf-8") as f:
                for i, file in enumerate(all_ts_files, 1):
                    file_path = file.replace("\\", "/")
                    f.write(f"file '{file_path}'\n")
                    # 每100个文件输出一次进度
                    if i % 100 == 0 or i == len(all_ts_files):
                        progress = (i / len(all_ts_files)) * 100
                        self._log(f"临时文件列表创建进度：{i}/{len(all_ts_files)} ({progress:.1f}%)")
            self._log("临时文件列表创建完成", "SUCCESS")
        except Exception as e:
            self._log(f"创建临时文件列表失败：{str(e)}", "ERROR")
            raise

    def _delete_parent_folder(self, parent_folder: str) -> bool:
        """删除上级文件夹（E:\视频录制的直接下级文件夹）"""
        self._log(f"开始删除原文件夹：{parent_folder}")
        try:
            if os.path.exists(parent_folder):
                # 计算文件夹大小（用于进度显示）
                total_size = 0
                for dirpath, dirnames, filenames in os.walk(parent_folder):
                    for f in filenames:
                        fp = os.path.join(dirpath, f)
                        total_size += os.path.getsize(fp)

                self._log(f"文件夹大小：{total_size / 1024 / 1024:.2f} MB，开始删除...")
                shutil.rmtree(parent_folder)
                self._log("文件夹删除成功", "SUCCESS")
                return True
            else:
                self._log("文件夹不存在，无需删除", "WARNING")
                return True
        except Exception as e:
            self._log(f"删除文件夹失败：{str(e)}", "ERROR")
        return False

    def start_merge_single(self):
        """合并当前选择的上级文件夹下的所有TS文件（带进度日志）"""
        self._clear_log()  # 合并前清空日志
        if not self.selected_parent_folder or not self.found_targets:
            self._log("未选择包含TS文件的文件夹，合并取消", "ERROR")
            messagebox.showwarning("警告", "请先选择包含TS文件的文件夹！")
            return

        # 获取最终输出路径和文件名
        output_path = self._get_output_path()
        final_filename = os.path.basename(output_path)
        ts_targets = self.found_targets[self.selected_parent_folder]
        parent_folder_name = os.path.basename(self.selected_parent_folder)

        # 收集所有TS文件
        all_ts_files = []
        for _, ts_files in ts_targets:
            all_ts_files.extend(ts_files)

        # 确认合并
        delete_confirm = ""
        if self.delete_after_merge.get():
            delete_confirm = "\n⚠️  合并成功后将自动删除原文件夹：" + parent_folder_name

        confirm = messagebox.askyesno(
            "确认开始合并",
            f"📹 即将合并文件夹：{parent_folder_name}\n"
            f"📁 包含 {len(all_ts_files)} 个TS文件（来自 {len(ts_targets)} 个子目录）\n"
            f"📄 输出文件名：{final_filename}\n"
            f"💾 保存位置：{os.path.dirname(output_path)}{delete_confirm}\n"
            f"\n是否开始合并？"
        )
        if not confirm:
            self._log("用户取消合并操作", "WARNING")
            return

        # 准备合并
        self._set_controls_state(tk.DISABLED)
        self.status_label.config(text=f"正在合并：{parent_folder_name}...")
        self.progress_bar.pack(fill=tk.X, padx=5, pady=5, expand=True)
        self.progress_var.set(10)
        self._log("=" * 50, "INFO")
        self._log(f"开始合并单个文件夹：{parent_folder_name}", "INFO")
        self._log(f"合并文件总数：{len(all_ts_files)} 个", "INFO")
        self._log(f"输出文件：{final_filename}", "INFO")
        self._log(f"保存路径：{os.path.dirname(output_path)}", "INFO")
        self._log(f"自动删除原文件夹：{'是' if self.delete_after_merge.get() else '否'}", "INFO")
        self._log("=" * 50, "INFO")
        self.root.update_idletasks()

        try:
            # 1. 创建临时文件列表（10% -> 30%）
            self.progress_var.set(10)
            temp_list_path = os.path.join(os.path.dirname(output_path),
                                          f"temp_ts_list_{os.path.splitext(final_filename)[0]}.txt")
            self._create_temp_file_list(all_ts_files, temp_list_path)
            self.progress_var.set(30)
            self._log("进度：30% - 临时文件列表创建完成", "INFO")

            # 2. 执行FFmpeg合并（30% -> 80%）
            self.progress_var.set(30)
            self._log("进度：30% - 开始执行FFmpeg合并（无损合并）", "INFO")
            self.status_label.config(text="正在合并视频...（无损合并，速度较快）")
            self.root.update_idletasks()

            command = [
                "ffmpeg",
                "-f", "concat",
                "-safe", "0",
                "-i", temp_list_path,
                "-c", "copy",
                "-bsf:a", "aac_adtstoasc",
                "-y",
                output_path
            ]

            self._log(f"FFmpeg命令：{' '.join(command)}", "INFO")
            self.progress_var.set(50)
            self._log("进度：50% - FFmpeg合并中...", "INFO")

            # 执行FFmpeg并捕获输出（实时日志）
            process = subprocess.Popen(
                command,
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
                text=True,
                encoding="utf-8",
                bufsize=1
            )

            # 实时读取FFmpeg输出
            while process.poll() is None:
                line = process.stdout.readline()
                if line:
                    self._log(f"FFmpeg输出：{line.strip()}", "INFO")

            # 检查FFmpeg执行结果
            if process.returncode == 0:
                self.progress_var.set(80)
                self._log("进度：80% - FFmpeg合并成功", "SUCCESS")
            else:
                raise Exception(f"FFmpeg执行失败（返回码：{process.returncode}）")

            # 3. 合并成功，删除原文件夹（80% -> 100%）
            delete_success = True
            if self.delete_after_merge.get():
                self.status_label.config(text=f"合并成功，正在删除原文件夹...")
                self._log("进度：80% - 开始删除原文件夹", "INFO")
                self.root.update_idletasks()
                delete_success = self._delete_parent_folder(self.selected_parent_folder)

            self.progress_var.set(100)
            delete_info = "（已自动删除原文件夹）" if delete_success and self.delete_after_merge.get() else ""
            self.status_label.config(text=f"合并成功：{final_filename} {delete_info}")
            self._log(f"进度：100% - 合并完成！{delete_info}", "SUCCESS")
            self._log(f"最终输出文件：{output_path}", "SUCCESS")

            messagebox.showinfo(
                "✅ 合并成功",
                f"视频合并完成！{delete_info}\n\n"
                f"源文件夹：{parent_folder_name}\n"
                f"输出文件：{final_filename}\n"
                f"保存位置：{os.path.dirname(output_path)}\n"
                f"\n是否打开保存文件夹？"
            )
            self._open_folder(os.path.dirname(output_path))

            # 重新扫描（删除后更新列表）
            self.scan_ts_folders()

        except Exception as e:
            self.status_label.config(text=f"❌ 合并失败：{parent_folder_name}")
            self._log(f"合并失败：{str(e)}", "ERROR")
            messagebox.showerror("合并失败", f"合并 {parent_folder_name} 时出错：\n{str(e)}\n\n原文件夹未删除，可重新尝试。")
            self.progress_var.set(0)
        finally:
            # 清理临时文件
            temp_list_path = os.path.join(os.path.dirname(output_path),
                                          f"temp_ts_list_{os.path.splitext(final_filename)[0]}.txt")
            if os.path.exists(temp_list_path):
                try:
                    os.remove(temp_list_path)
                    self._log(f"临时文件已清理：{temp_list_path}", "INFO")
                except Exception as e:
                    self._log(f"清理临时文件失败：{str(e)}", "WARNING")

            # 恢复控件状态
            self._set_controls_state(tk.NORMAL)
            self.progress_bar.pack_forget()
            self.root.update_idletasks()

    def start_merge_all(self):
        """批量合并所有上级文件夹下的TS文件（带进度日志）"""
        self._clear_log()  # 合并前清空日志
        if not self.found_targets:
            self._log("未找到任何包含TS文件的文件夹，批量合并取消", "ERROR")
            messagebox.showwarning("警告", "未找到任何包含TS文件的文件夹！")
            return

        # 确认批量合并
        delete_confirm = ""
        if self.delete_after_merge.get():
            delete_confirm = "\n⚠️  所有文件夹合并成功后将自动删除原文件夹！"

        aa = 'E:\\视频录制目录'
        confirm = messagebox.askyesno(
            "确认批量合并",
            f"即将批量合并 {len(self.found_targets)} 个文件夹！\n"
            f"输出文件名：各文件夹名+.mp4（自动处理冲突）\n"
            f"保存模式：{aa if self.save_mode.get() == 'same_dir' else f'统一目录：{self.custom_save_path}'}{delete_confirm}\n"
            f"\n是否继续？"
        )
        if not confirm:
            self._log("用户取消批量合并操作", "WARNING")
            return

        self._set_controls_state(tk.DISABLED)
        self.status_label.config(text=f"开始批量合并 {len(self.found_targets)} 个文件夹...")
        self.progress_bar.pack(fill=tk.X, padx=5, pady=5, expand=True)
        self.progress_var.set(0)
        self._log("=" * 50, "INFO")
        self._log(f"开始批量合并：共 {len(self.found_targets)} 个文件夹", "INFO")
        self._log(f"保存模式：{aa if self.save_mode.get() == 'same_dir' else '自定义目录'}", "INFO")
        self._log(f"自动删除原文件夹：{'是' if self.delete_after_merge.get() else '否'}", "INFO")
        self._log("=" * 50, "INFO")
        self.root.update_idletasks()

        success_count = 0
        fail_folders = []
        deleted_count = 0
        total_folders = len(self.found_targets)

        try:
            for idx, (parent_folder, ts_targets) in enumerate(self.found_targets.items(), 1):
                parent_folder_name = os.path.basename(parent_folder)
                current_progress = (idx - 1) / total_folders * 100
                self.progress_var.set(current_progress)
                self.status_label.config(text=f"正在合并 ({idx}/{total_folders})：{parent_folder_name}")
                self._log(f"\n{'=' * 30} 处理文件夹 {idx}/{total_folders}：{parent_folder_name} {'=' * 30}", "INFO")
                self.root.update_idletasks()

                # 收集所有TS文件
                all_ts_files = []
                for _, ts_files in ts_targets:
                    all_ts_files.extend(ts_files)
                self._log(f"找到 {len(all_ts_files)} 个TS文件", "INFO")

                # 获取输出路径（使用文件夹名作为默认文件名）
                output_path = self._get_output_path(
                    parent_folder=parent_folder,
                    custom_filename=parent_folder_name  # 强制使用直接下级文件夹名
                )
                final_filename = os.path.basename(output_path)
                self._log(f"输出文件：{final_filename}", "INFO")

                # 创建临时文件列表
                temp_list_path = os.path.join(os.path.dirname(output_path), f"temp_ts_list_{idx}.txt")
                try:
                    self._create_temp_file_list(all_ts_files, temp_list_path)
                except Exception as e:
                    self._log(f"创建临时文件失败，跳过该文件夹：{str(e)}", "ERROR")
                    fail_folders.append(f"{parent_folder_name}（创建临时文件失败）")
                    continue

                # 执行合并
                self._log("开始执行FFmpeg合并", "INFO")
                command = [
                    "ffmpeg",
                    "-f", "concat",
                    "-safe", "0",
                    "-i", temp_list_path,
                    "-c", "copy",
                    "-bsf:a", "aac_adtstoasc",
                    "-y",
                    output_path
                ]

                process = subprocess.Popen(
                    command,
                    stdout=subprocess.PIPE,
                    stderr=subprocess.STDOUT,
                    text=True,
                    encoding="utf-8",
                    bufsize=1
                )

                # 实时读取FFmpeg输出
                while process.poll() is None:
                    line = process.stdout.readline()
                    if line:
                        self._log(f"FFmpeg输出：{line.strip()}", "INFO")

                # 清理临时文件
                if os.path.exists(temp_list_path):
                    os.remove(temp_list_path)
                    self._log("临时文件已清理", "INFO")

                # 处理合并结果
                if process.returncode == 0:
                    success_count += 1
                    self._log(f"文件夹 {parent_folder_name} 合并成功", "SUCCESS")
                    # 合并成功，删除原文件夹
                    if self.delete_after_merge.get():
                        if self._delete_parent_folder(parent_folder):
                            deleted_count += 1
                else:
                    error_msg = f"FFmpeg返回码：{process.returncode}"
                    self._log(f"文件夹 {parent_folder_name} 合并失败：{error_msg}", "ERROR")
                    fail_folders.append(f"{parent_folder_name}({error_msg})")

                # 更新进度
                current_progress = idx / total_folders * 100
                self.progress_var.set(current_progress)
            self._log(f"文件夹 {parent_folder_name} 处理完成，当前进度：{current_progress:.1f}%", "INFO")
        except: pass

            # self._log(f"文件夹 {parent_folder_name} 处理完成，当前进度：{current_progress:.1f}%", "INFO")
        # 批量合并完成
        self.progress_var.set(100)
        self.status_label.config(text="批量合并完成！")
        self._log("\n" + "=" * 50, "INFO")
        self._log("批量合并完成！", "SUCCESS")

        # 构建结果信息
        delete_info = f"\n自动删除成功：{deleted_count} 个文件夹" if self.delete_after_merge.get() else ""
        result_text = f"✅ 批量合并结果汇总：\n"
        result_text += f"总文件夹数：{total_folders}\n"
        result_text += f"成功合并：{success_count}\n"
        result_text += f"合并失败：{len(fail_folders)}{delete_info}\n"

        if fail_folders:
            result_text += f"\n失败文件夹（最多显示5个）：\n" + "\n".join(fail_folders[:5])

        self._log(f"结果汇总：{result_text}", "SUCCESS")
        messagebox.showinfo("批量合并完成", result_text)

        # 打开保存目录
        save_dir = self.default_root_path if self.save_mode.get() == "same_dir" else self.custom_save_path
        if messagebox.askyesno("打开文件夹", "是否立即打开保存目录？"):
            self._open_folder(save_dir)

        # 重新扫描（删除后更新列表）
        self.scan_ts_folders()

        # 恢复控件状态
        self._set_controls_state(tk.NORMAL)
        self.progress_bar.pack_forget()
        self.root.update_idletasks()

    def _set_controls_state(self, state: str):
        """设置所有控件的启用/禁用状态（修复：使用self.save_frame）"""
        self.open_root_btn.config(state=state)
        self.rescan_btn.config(state=state)
        self.parent_folder_combobox.config(state="normal" if state == tk.NORMAL else "disabled")
        self.select_custom_save_btn.config(state=state if self.save_mode.get() == "custom_dir" else tk.DISABLED)
        self.merge_single_btn.config(state=state)
        self.merge_all_btn.config(state=state)
        self.open_output_btn.config(state=state if self.selected_parent_folder else tk.DISABLED)
        self.filename_entry.config(state=state)  # 禁用时不允许修改文件名

        # 单选按钮和复选框始终启用（修复：使用self.save_frame）
        for child in self.save_frame.winfo_children():
            if isinstance(child, (ttk.Radiobutton, ttk.Checkbutton)):
                child.config(state=state)

    def _open_folder(self, folder_path: Optional[str]):
        """跨平台打开文件夹"""
        if not folder_path or not os.path.exists(folder_path):
            self._log(f"无法打开文件夹：{folder_path}（不存在或未选择）", "WARNING")
            messagebox.showwarning("警告", "文件夹不存在或未选择！")
            return

        try:
            self._log(f"打开文件夹：{folder_path}")
            if sys.platform == "win32":
                os.startfile(folder_path)
            elif sys.platform == "darwin":
                subprocess.run(["open", folder_path])
            else:
                subprocess.run(["xdg-open", folder_path])
        except Exception as e:
            self._log(f"打开文件夹失败：{str(e)}", "ERROR")
            messagebox.showwarning("警告", f"无法打开文件夹：{str(e)}")


def main():
    # 检查ffmpeg是否安装
    try:
        subprocess.run(
            ["ffmpeg", "-version"],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            check=True,
            timeout=5
        )
    except (subprocess.CalledProcessError, FileNotFoundError, TimeoutError):
        messagebox.showerror(
            "错误 - 未找到ffmpeg",
            "❌ 无法找到ffmpeg工具！\n\n请先安装ffmpeg并配置环境变量：\n"
            "• Windows：下载后将bin目录添加到系统环境变量\n"
            "• Mac：打开终端执行 brew install ffmpeg\n"
            "• Linux：打开终端执行 sudo apt install ffmpeg"
        )
        sys.exit(1)

    # 启动GUI
    root = tk.Tk()
    app = TSMergerGUI(root)
    root.mainloop()


if __name__ == "__main__":
    main()